/*
 *  Cero Project - Copyright   2006 The Cero Developement Team 
 *  (Michael Laguerre, Camille Roux, Matthieu Segret, Mathieu Sivade) 
 * 
 *  This program is free software; you can redistribute it and/or modify it 
 *  under the terms of the GNU General Public License as published by the Free 
 *  Software Foundation; either version 2 of the License, or (at your option) 
 *  any later version.
 * 
 *  This program is distributed in the hope that it will be useful, but 
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 *  for more details.
 */

package org.ceroproject.ui.commandline;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.ceroproject.UnsupportedPluginException;
import org.ceroproject.games.Action;
import org.ceroproject.games.ActionListener;
import org.ceroproject.games.Game;
import org.ceroproject.games.GameEvent;
import org.ceroproject.games.GameListener;
import org.ceroproject.games.Player;
import org.ceroproject.games.Zone;
import org.ceroproject.ui.ChoiceMaker;
import org.ceroproject.ui.InterfaceModule;
import org.ceroproject.ui.UserInterface;
import org.ceroproject.ui.commandline.CommandLister;
import org.ceroproject.ui.commandline.ErrorOutputWriter;
import org.ceroproject.ui.commandline.InputReader;
import org.ceroproject.ui.commandline.OutputWriter;
import org.ceroproject.ui.commandline.StandardErrorOutput;
import org.ceroproject.ui.commandline.StandardInput;
import org.ceroproject.ui.commandline.StandardOutput;



/**
 * Command Line
 * 
 * @author Camille Roux
 * 
 */
public class CommandLineLauncher implements GameListener, UserInterface {

	/** command list */
	private static CommandLister commands = new CommandLister();

	/** standard input */
	private static InputReader input = new StandardInput();

	/** standard output */
	private static OutputWriter output = new StandardOutput();

	/** standard error output */
	private static ErrorOutputWriter errorOutput = new StandardErrorOutput();

	private static Game game;

	private static Player currentPlayer = null;

	/** Command plugins */
	private Collection<InterfaceModule> commandmodules = new HashSet<InterfaceModule>();

	private boolean activated = false;

	public CommandLineLauncher() {
		// FIXME : supprimer cette ligne
		PorkassChoiceMaker.getInstance();
	}

	public void gameStart(GameEvent e) {
		System.out.println("Game started");
		CommandLine commandLine = new CommandLine();
		for (Zone zone : e.getGame().getZones()) {
			zone.addZoneListener(commandLine);
		}
		for (Player player : e.getGame().getPlayers())
			for (Zone zone : player.getZones())
				zone.addZoneListener(commandLine);
		game = e.getGame();
		currentPlayer = game.getPlayers().get(0);

		for (InterfaceModule im : commandmodules) {
			if (im instanceof ActionListener)
				game.addActionListener((ActionListener) im);
		}
	}

	public void listen() {
		System.out.println("Welcome on the Cero Command Line System");

		while (true) {
			// input reading
			String commandString = input.readLine();
			execute(commandString);
			output.println("");
		}

	}

	public void execute(String commandString) {
		Command command = CommandCutter.commandExtract(commandString, commands);
		if (command == null)
			output.println("This command doesn't exist");
		else {
			if (!command.executer(CommandCutter.parametersExtract(
					commandString, command)))
				output.println("The command has failed");
		}
	}

	public void gameEnd(GameEvent e) {
		game = null;
		System.out.println("Game has ended");
	}

	public void onPlayerPlayed(Action action) {
		System.out.println("a player played");

	}

	public void onExecute(Action action) {

	}

	/**
	 * @return Returns the input.
	 */
	public static InputReader getInput() {
		return input;
	}

	/**
	 * @return Returns the output.
	 */
	public static OutputWriter getOutput() {
		return output;
	}

	/**
	 * @return Returns the error output.
	 */
	public static ErrorOutputWriter getErrorOutput() {
		return errorOutput;
	}

	/**
	 * @return Return game.
	 */
	public static Game getGame() {
		return game;
	}

	public void setActivated(boolean state) {
		if (state) {
			activated = true;
			listen();
		}
		// else we should desactivate the interface
	}

	/**
	 * @return Returns the currentPlayer.
	 */
	public static Player getCurrentPlayer() {
		return currentPlayer;
	}

	/**
	 * @param currentPlayer
	 *            The currentPlayer to set.
	 */
	public static void setCurrentPlayer(Player currentPlayer) {
		CommandLineLauncher.currentPlayer = currentPlayer;
	}

	/**
	 * @return Returns the commands.
	 */
	public static CommandLister getCommands() {
		return commands;
	}

	public void addInterfaceModule(InterfaceModule module)
			throws UnsupportedPluginException {
		commandmodules.add(module);
	}

	public void removeInterfaceModule(InterfaceModule module) {
		desactivateInterfaceModule(module);
		commandmodules.remove(module);
	}

	public void activateInterfaceModule(InterfaceModule module) {
		if (commandmodules.contains(module)) {
			module.onActivate();
			if (module instanceof Command) {
				commands.ajouterCommande((Command) module);
			}
		}
	}

	public void desactivateInterfaceModule(InterfaceModule module) {
		if (module instanceof Command && commandmodules.contains(module)) {
			commands.enleverCommande((Command) module);
			module.onDesactivated();
		}
	}

	public Collection<InterfaceModule> getInterfaceModules() {
		return new ArrayList<InterfaceModule>(commandmodules);
	}

	public ChoiceMaker getChoiceMaker() throws UnsupportedOperationException {
		return PorkassChoiceMaker.getInstance();
	}

	public boolean isActivated() {
		return activated;
	}

	public String getUserInterfaceName() {
		return "Cero incomplete command line interface :P";
	}

}

//FIXME : delete this and make something nice instead ! 
class PorkassChoiceMaker extends JFrame implements ChoiceMaker, ChangeListener,
		java.awt.event.ActionListener {

	private static final long serialVersionUID = 5029132901734836265L;

	private static PorkassChoiceMaker instance = new PorkassChoiceMaker();

	private JLabel question = new JLabel();

	private Map<JCheckBox, Object> availableChoices = new HashMap<JCheckBox, Object>();

	private List<Object> choices = new ArrayList<Object>();

	private Thread originThread = null;

	private JButton btValidate = new JButton("OK");

	private int choicesNumber;

	public List<Object> chooseFrom(String question,
			Collection<? extends Object> choices, int quantity) {
		this.getContentPane().removeAll();
		this.getContentPane().setLayout(new FlowLayout());

		this.question.setText(question);
		this.getContentPane().add(this.question);

		availableChoices.clear();
		JCheckBox checkbox;
		for (Object o : choices) {
			checkbox = new JCheckBox(o.toString());
			availableChoices.put(checkbox, o);
			checkbox.addChangeListener(this);
			this.getContentPane().add(checkbox);
		}

		btValidate.setEnabled(false);
		btValidate.addActionListener(this);
		this.getContentPane().add(btValidate);

		choicesNumber = quantity;

		originThread = Thread.currentThread();
		this.setVisible(true);
		// blocks the thread here, so we can wait for the user's answer
		while (true) {
			try {
				Thread.sleep(100000000L);
			} catch (InterruptedException e) {
				if (choices.size() == quantity)
					break;
			}
			// FIXME : breakable code : if the thread gets interrupted out of
			// its sleeping state
		}
		this.setVisible(false);
		originThread = null;

		return this.choices;
	}

	public void stateChanged(ChangeEvent event) {
		if (event.getSource() instanceof JCheckBox) {
			JCheckBox cb = (JCheckBox) event.getSource();
			Object o = availableChoices.get(event.getSource());
			if (cb.isSelected()) {
				if (!choices.contains(o))
					choices.add(o);
			} else
				choices.remove(o);
			btValidate.setEnabled(choices.size() == choicesNumber);
		}
	}

	public void actionPerformed(ActionEvent arg0) {
		if (arg0.getSource() == btValidate) {
			if (originThread != null)
				originThread.interrupt();
		}
	}

	public static PorkassChoiceMaker getInstance() {
		return instance;
	}

}
